package com.xinnet.core.log;

import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.spi.LoggingEvent;
import org.json.JSONException;
import org.json.JSONObject;
import org.log4mongo.BsonAppender;
import org.log4mongo.contrib.JvmMonitor;

import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoException;
import com.mongodb.util.JSON;

/**
 * 通过mongURI参数配置mongdb的参数
 */
public class MongoURILayoutAppender extends BsonAppender {

	private final static String DEFAULT_MONGO_DB_COLLECTION_NAME = "logevents";
	
	private static final Log log = LogFactory.getLog(MongoURILayoutAppender.class);

	private String mongoURI;
	private String collectionName = DEFAULT_MONGO_DB_COLLECTION_NAME;
	private String jvmMonitor = "false";
	private String jvmMonitorPeriodSeconds = "60";
	
	private String username; //mongo用户名
	private String password; //mongo密码
	
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	private MongoClient mongo = null;
	private DBCollection collection = null;
	private boolean initialized = false;

	private Pattern pattern = Pattern.compile("^(\\{.*?\\})#");

	/**
	 * 后台写日志的线程个数
	 */
	// private int threadCount = 2;
	// private LinkedBlockingQueue<Runnable> workQueue;
	// private int maxWorkSize = 1000;
	// private static ThreadPoolExecutor executorService;

	/**
	 * @see org.apache.log4j.Appender#requiresLayout()
	 */
	public boolean requiresLayout() {
		return (true);
	}

	/** 初始化线程池 */
	// public void initThreadPoolExecutor(){
	// workQueue = new LinkedBlockingQueue<Runnable>(2*maxWorkSize);
	// executorService = new ThreadPoolExecutor(threadCount, threadCount,
	// 0L, TimeUnit.MILLISECONDS,
	// workQueue);
	// }
	/** 性能监控初始化 */
	private void initJvmMonitor() {
		if (!jvmMonitor.equals("true"))
			return;
		if (jvmMonitorPeriodSeconds != null
				&& !jvmMonitorPeriodSeconds.equals("")
				&& jvmMonitorPeriodSeconds.matches("[0-9]*"))
			JvmMonitor.getInstance(Integer.parseInt(jvmMonitorPeriodSeconds));
		else
			JvmMonitor.getInstance();
	}

	/** mongodb初始化 */
	private void initMongodb() throws MongoException, UnknownHostException {
		if (mongo != null)
			close();
		
		MongoClientURI uri = new MongoClientURI(mongoURI);
		mongo = new MongoClient(uri);
		DB db = mongo.getDB(uri.getDatabase());
		
		if(StringUtils.isNotBlank(username)){
			boolean authFlag = db.authenticate(username, password.toCharArray());
//			System.out.println("auth授权:"+authFlag);
			if(!authFlag){
				log.error("mongo授权验证失败,请检查授权账号信息是否正确，该用户名："+username+",密码："+password);
			}
		}
		
		setCollection(db.getCollection(collectionName));
	}
	

	/**
	 * @see org.apache.log4j.AppenderSkeleton#activateOptions()
	 */
	@Override
	public void activateOptions() {
		try {
			// initThreadPoolExecutor();
			initJvmMonitor();
			initMongodb();
			initialized = true;
		} catch (Exception e) {
			errorHandler.error(
					"Unexpected exception while initialising MongoDbAppender.",
					e, ErrorCode.GENERIC_FAILURE);
		}
	}

	public void setCollection(final DBCollection collection) {
		assert collection != null : "collection must not be null.";
		this.collection = collection;
	}

	public String getCollectionName() {
		return collectionName;
	}

	public void close() {
		if (mongo != null) {
			collection = null;
			mongo.close();
		}
	}

	public void setCollectionName(final String collectionName) {
		assert collectionName != null : "collection must not be null";
		assert collectionName.trim().length() > 0 : "collection must not be empty or blank";
		this.collectionName = collectionName;
	}

	@Override
	protected void append(LoggingEvent loggingEvent) {
		loggingEvent.getNDC();
		loggingEvent.getThreadName();
		// Get a copy of this thread's MDC.
		loggingEvent.getMDCCopy();
		loggingEvent.getRenderedMessage();
		loggingEvent.getThrowableStrRep();

		DBObject bson = null;
		String json = layout.format(loggingEvent);
		if (json.length() > 0) {
			Object obj = JSON.parse(json);
			if (obj instanceof DBObject) {
				bson = (DBObject) obj;
				// 将log中的message解析
				bson.putAll(getMap(loggingEvent));
				bson.put("timestamp", new Date(loggingEvent.getTimeStamp()));
			}
		}
		append(bson);

	}

	private Map<String, Object> getMap(LoggingEvent loggingEvent) {
		PatternLayout infoLayout = new PatternLayout();
		infoLayout.setConversionPattern("%m");
		String msg = infoLayout.format(loggingEvent);

		/*
		 * infoLayout.setConversionPattern("%X{"+CustomLogger.DB_PREFIX+"}");
		 * String pfx = infoLayout.format(loggingEvent);
		 */

		Matcher m = pattern.matcher(msg);

		Map<String, Object> map = new HashMap<String, Object>();
		if (m.find()) {
			String json = m.group(1);
			if (json != null) {
				try {
					JSONObject jo = new JSONObject(json);
					if (jo.length() > 0) {

						Iterator iterator = jo.keys();
						while (iterator.hasNext()) {
							String key = iterator.next().toString();
							map.put(key, jo.get(key));
						}
					}
				} catch (JSONException e) {
					errorHandler.error("解析" + json + "失败", e,
							ErrorCode.GENERIC_FAILURE);
				}
			}
		}
		/*
		 * if(pfx != null && !pfx.trim().equals("")){
		 * map.put(CustomLogger.DB_PREFIX, pfx); }
		 */
		return map;
	}

	@SuppressWarnings("unchecked")
	private Map _check(Map map) {

		if (map == null || map.isEmpty()) {
			return map;
		}

		Map m = new HashMap(map);
		Iterator<Entry> iterator = m.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry e = iterator.next();
			String key = e.getKey().toString();
			if (key != null && key.contains(".")) {
				String newKey = key.replaceAll("\\.", "_");
				m.put(newKey, e.getValue());
				m.remove(key);
			}
		}

		return m;
	}

	@Override
	public void append(DBObject bson) {
		if (initialized && bson != null) {
			try {
				getCollection().insert(bson);
			} catch (MongoException e) {
				errorHandler.error("Failed to insert document to MongoDB", e,
						ErrorCode.WRITE_FAILURE);
			} catch (Exception e) {
				errorHandler.error("Failed to insert document to MongoDB", e,
						ErrorCode.WRITE_FAILURE);
			}
		}
	}

	// @Override
	// protected void append(final LoggingEvent loggingEvent) {
	// Map MDCdata = loggingEvent.getProperties();
	// if (workQueue.size() < maxWorkSize) {
	// executorService.execute(new Runnable() {
	// @Override
	// public void run() {
	// try {
	// _append(loggingEvent);
	// } catch (Exception e) {
	// //ingore errors
	// }
	// }
	// });
	// }
	// }

	// private void _append(final LoggingEvent loggingEvent) {
	// if (isInitialized()) {
	// DBObject bson = null;
	// String json = layout.format(loggingEvent);
	//
	// if (json.length() > 0) {
	// Object obj = JSON.parse(json);
	// if (obj instanceof DBObject) {
	// bson = (DBObject) obj;
	// //将MDC中的属性赋值给mongodb数据对象
	// bson.putAll(loggingEvent.getProperties());
	// }
	// }
	//
	// if (bson != null) {
	// try {
	// //由于timestamp被转成了string，所以重新写入时间
	// bson.put("timestamp",new Date());
	// getCollection().insert(bson);
	// } catch (MongoException e) {
	// errorHandler.error("Failed to insert document to MongoDB",
	// e, ErrorCode.WRITE_FAILURE);
	// }
	// }
	// }
	// }

	public boolean isInitialized() {
		return initialized;
	}

	protected DBCollection getCollection() {
		return collection;
	}

	public String getMongoURI() {
		return mongoURI;
	}

	public void setMongoURI(String mongoURI) {
		assert mongoURI != null : "mongoURI must not be null";
		assert mongoURI.trim().length() > 0 : "mongoURI must not be empty or blank";
		this.mongoURI = mongoURI;
	}

	public String getJvmMonitor() {
		return jvmMonitor;
	}

	public void setJvmMonitor(String jvmMonitor) {
		this.jvmMonitor = jvmMonitor;
	}

	public String getJvmMonitorPeriodSeconds() {
		return jvmMonitorPeriodSeconds;
	}

	public void setJvmMonitorPeriodSeconds(String jvmMonitorPeriodSeconds) {
		this.jvmMonitorPeriodSeconds = jvmMonitorPeriodSeconds;
	}

}
